namespace jQube.Web.UI
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using jQube.Resources;
    using System.Web;
    using System.IO;

    internal sealed class PageRequestManager
    {
        private const string JSonContentType = "application/json";

        private ScriptManager scriptManager;
        private PageRenderingManager pageRenderingManager;
        private Dictionary<Type, string> requiredScripts;
        private Dictionary<Type, string> requiredStyleSheet;
        private List<string> initilizationScripts;

        private static readonly Version MinimumW3CDomVersion = new Version(1, 0);
        private static readonly Version MinimumEcmaScriptVersion = new Version(1, 0);

        public PageRequestManager(ScriptManager scriptManager)
        {
            this.ScriptManager = scriptManager;
            this.requiredScripts = new Dictionary<Type, string>();
            this.requiredStyleSheet = new Dictionary<Type, string>();
            this.initilizationScripts = new List<string>();
            this.CheckBrowserCapabilities();
        }

        private void CheckBrowserCapabilities()
        {
            if (this.ScriptManager.EnablePartialRendering && !this.ScriptManager.SupportsPartialRenderingSetByUser)
            {
                IHttpBrowserCapabilities browser = this.ScriptManager.IPage.Request.Browser;

                bool supportPartialRendering = (
                        browser.W3CDomVersion >= MinimumW3CDomVersion &&
                        browser.EcmaScriptVersion >= MinimumEcmaScriptVersion &&
                        browser.SupportsCallback);

                if (supportPartialRendering)
                {
                    supportPartialRendering = !this.ScriptManager.GetEnableLegacyRendering();
                }

                this.ScriptManager.SupportsPartialRendering = supportPartialRendering;
            }
        }

        public ScriptManager ScriptManager
        {
            get
            {
                return this.scriptManager;
            }
            set
            {
                this.scriptManager = value;
            }
        }

        public PageRenderingManager PageRenderingManager
        {
            get
            {
                if (this.pageRenderingManager == null)
                {
                    this.pageRenderingManager = new PageRenderingManager(this);
                }

                return this.pageRenderingManager;
            }
        }

        public void AddRequiredScript(Type type)
        {
            RequiredScriptAttribute[] typeRequiredScripts =
                (RequiredScriptAttribute[])type.GetCustomAttributes(typeof(RequiredScriptAttribute), true);

            foreach (RequiredScriptAttribute typeRequiredScript in typeRequiredScripts)
            {
                if (!string.IsNullOrEmpty(typeRequiredScript.ScriptResourceName))
                {
                    if (!this.requiredScripts.ContainsKey(type))
                        this.requiredScripts.Add(type, typeRequiredScript.ScriptResourceName);
                }
                else if (string.IsNullOrEmpty(typeRequiredScript.ScriptResourceName) &&
                        typeRequiredScript.ExtendedType != null)
                {
                    this.AddRequiredScript(typeRequiredScript.ExtendedType);
                }
            }
        }

        public void AddRequiredStyleSheet(Type type)
        {
            RequiredStyleSheetAttribute[] typeRequiredStyleSheets =
                (RequiredStyleSheetAttribute[])type.GetCustomAttributes(typeof(RequiredStyleSheetAttribute), true);

            foreach (RequiredStyleSheetAttribute typeRequiredStyleSheet in typeRequiredStyleSheets)
            {
                if (!string.IsNullOrEmpty(typeRequiredStyleSheet.StyleSheetResourceName))
                {
                    if (!this.requiredStyleSheet.ContainsKey(type))
                        this.requiredStyleSheet.Add(type, typeRequiredStyleSheet.StyleSheetResourceName);
                }
                else if (string.IsNullOrEmpty(typeRequiredStyleSheet.StyleSheetResourceName) &&
                        typeRequiredStyleSheet.ExtendedType != null)
                {
                    this.AddRequiredStyleSheet(typeRequiredStyleSheet.ExtendedType);
                }
            }
        }

        public void AddInitilizationScript(string script)
        {
            this.initilizationScripts.Add(script);
        }

        public void OnPageInitComplete(object sender, EventArgs e)
        {
            ScriptManager scriptManager = this.ScriptManager;

            if (scriptManager.IsAsyncPostBack && !scriptManager.SupportsPartialRendering)
            {
                throw new InvalidOperationException(AtlasWeb.ScriptManager_AsyncPostBackNotInPartialRenderingMode);
            }
        }

        public void OnPagePreRender(object sender, EventArgs e)
        {
            this.ScriptManager.IPage.ClientScript.RegisterHiddenField(ScriptManager.IsAsyncPostBackHiddenField, "false");
            this.ScriptManager.IPage.ClientScript.RegisterHiddenField(ScriptManager.UpdatePanelsToUpgradeHiddenField, string.Empty);

            if (!this.ScriptManager.IsAsyncPostBack)
            {
                this.LoadBaseScripts();
                this.LoadRequiredScripts();
                this.LoadRequiredStyleSheet();
            }

            this.PageRenderingManager.OnPagePreRender(sender, e);
        }

        public void OnPagePreRenderComplete(object sender, EventArgs e)
        {
            StringBuilder script = new StringBuilder(
@"
<div id=""scriptsWrapper"" style=""display:none;"">");

            if (!this.ScriptManager.IsAsyncPostBack)
            {
                script.Append(@"
<script language=""javascript"" type=""text/javascript"">
//<![CDATA[
    $(document).ready(function () {
        $(""#" + this.ScriptManager.ClientID + @""").scriptManager({ form: """ + this.ScriptManager.Page.Form.ClientID + @""" });
");

                foreach (string initScript in this.initilizationScripts)
                {
                    script.AppendLine(string.Format(@"      {0}", initScript));
                }

                script.Append(
    @"  });
//]]>
</script>");

            }
            else
            {
                //TODO: Add scripts at runtime in IsAsyncPostBack == true
                script.Append(@"
<script language=""javascript"" type=""text/javascript"">
//<![CDATA[
    alert('hola mundo');
//]]>
</script>");
            }

            script.Append(@"
</div>");

            this.ScriptManager.IPage.ClientScript.RegisterStartupScript(
                typeof(Page), "ScriptManagerInitilizer", script.ToString(), false);
        }

        public void OnPageError(object sender, EventArgs e)
        {
            this.PageRenderingManager.OnPageError(sender, e);
        }

        #region "Scripts Loading"
        private void LoadBaseScripts()
        {
            if (this.ScriptManager.Debug)
            {
                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery-1.8.2.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.activity-indicator-1.0.0.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.pageRequestManager-1.0.0.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.scriptManager-1.0.0.js"));
            }
            else
            {
                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery-1.8.2.min.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.activity-indicator-1.0.0.min.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.pageRequestManager-1.0.0.js"));

                this.LoadScriptReference(this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                    typeof(AssemblyResourceFinder), "jQube.Resources.jquery.scriptManager-1.0.0.js"));
            }
        }

        private void LoadRequiredScripts()
        {
            foreach (KeyValuePair<Type, string> requiredScript in this.requiredScripts)
            {
                string scriptReferenceUrl = this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                typeof(AssemblyResourceFinder), requiredScript.Value);

                this.LoadScriptReference(scriptReferenceUrl);
            }

            foreach (ScriptReference scriptReference in this.ScriptManager.ScriptReferences)
            {
                if (!string.IsNullOrWhiteSpace(scriptReference.Path))
                {
                    this.LoadScriptReference(scriptReference.GetPathRelativeToPage(this.ScriptManager.IPage));
                }
                else
                {
                    this.LoadScriptReference(scriptReference.GetWebResourceUrl(this.ScriptManager.IPage));
                }
            }
        }

        private void LoadScriptReference(string scriptReferenceUrl)
        {
            HtmlGenericControl scriptReference = new HtmlGenericControl("script");

            scriptReference.Attributes.Add("src", scriptReferenceUrl);
            scriptReference.Attributes.Add("type", "text/javascript");

            this.ScriptManager.IPage.Header.Controls.Add(scriptReference);
        }
        #endregion

        #region "StyleSheets Loading"
        private void LoadRequiredStyleSheet()
        {
            foreach (KeyValuePair<Type, string> requiredStyleSheet in this.requiredStyleSheet)
            {
                this.LoadStyleSheetLink(requiredStyleSheet.Value);
            }
        }

        private void LoadStyleSheetLink(string resourceName)
        {
            HtmlLink styleSheetLink = new HtmlLink();

            string styleSheetUri = this.ScriptManager.Page.ClientScript.GetWebResourceUrl(
                typeof(AssemblyResourceFinder), resourceName);

            //styleSheetUri = this.ScriptManager.Page.ResolveClientUrl(styleSheetUri);

            styleSheetLink.Href = styleSheetUri;
            styleSheetLink.Attributes.Add("rel", "stylesheet");
            styleSheetLink.Attributes.Add("type", "text/css");

            this.ScriptManager.IPage.Header.Controls.Add(styleSheetLink);
        }
        #endregion
    }
}
